package statistics;

import java.util.Random;

/**
 * This class represents a Mix of Binomial Distributions.
 */

public class MixBinomialDistribution extends DiscreteDistribution {

  private double p;
  private int  k;
  private double q;

  /**
   * Constructs a Mix of Binomial Distributions with parameter k,p and k+1,p. You must also specify the
   * random seed that will be used to realize random variables with this distribution.
   * @param expect the expectation of the Mix of Binomial Distributions.
   * @param a fit a mix of Binomial Distributions when a < 1.
   * @param random the randomgenerator.
   */

  public MixBinomialDistribution(double expect, double a, Random random) {
    this.random = random;
    k = ((int) Math.floor(-1.0/a));
    q = (1 + a*(1 + k) + Math.sqrt(-a*k*(1 + k) - k))/(1 + a);
    p = expect/(k + 1 - q);
  }

  /**
   * Returns the expectation of a Mix of Binomial distributions with parameters k,p and k+1,p.
   */

  public double expectation() {
    return q*k*p + (1-q)*(k+1)*p;
  }

  /**
   * Returns the variance of a Mix of Binomial distributions with parameters k,p and k+1,p.
   */

  public double variance() {
    return q*k*p*(1-p) + (1-q)*(k+1)*p*(1-p);
  }

  private double binom(int kk, double pp, int x) {
    if (x > kk) return 0.0;
    double denom = 1.0;
    double nom = 1.0;
    // k * ... * (x+1)
    for (int i = kk; i > x; i--) {
      denom = denom * i;
    }
    // (k-x)!
    for (int i = kk-x; i > 1; i--) {
      nom = nom * i;
    }
    return denom/nom * Math.pow(pp, x) * Math.pow(1.0-pp, kk-x);
  }

  public double probabilityFunction(int x) {
    return q*binom(k, p, x)+(1.0-q)*binom(k+1, p, x);
  }

  /**
   * Returns a realization of a random variable with a Mix of Binomial distributions.
   * The realizations are even.
   */

  public double nextRandom() {
     double u1 = random.nextDouble();
     double sum = 0;
     if(u1 < q) {
        for(int i = 0; i < k;i++) {
           double u = random.nextDouble();
           if(u <= p) {
              sum = sum + 1;
           }//end if
        }//end for
     }//end if
     else {
        for(int i = 0; i < k+1;i++) {
           double u = random.nextDouble();
           if(u <= p) {
              sum = sum + 1;
           }//if
        }//for
     }//else
     return sum;
  }
}
